﻿from platform.jutils import *

#Скрипт содержит набор функций и переменных, которые фактически определяют правила крестиков-ноликов
#правила не самодостаточны, но могут применяться в "Игре", "АИ", или клиентом - для получения правильности ходов, 
#критериев победы, или генерации начальных условий

Xchar = 'X';
Ochar = 'O';
Fchar = '-';
NBchar = 'N'; #никто не выиграл

#создает пустое поле заданного размера (напр 3*3 при l==3)
def createField(l):
	return [Fchar] * l * l;

#Возвращает литеру - чей сейчас ход (X, O или -) чтобы понять чей ход просто смотрит чьих меток больше, если поровну, то первым всегда ходит "Х"
def whoseTurn(fld): 
	if (fld.count(Xchar) > fld.count(Ochar)): 
		return Ochar;
	return Xchar;

#Вычисляет размерность поля, как квадратный корень общего размера 
def getLfield(fld):
	return int(len(fld)**0.5);

#Отвечает на вопрос - можно ли по правилам поставить знак 'who' в точку (x, y)
#проверяет - того ли юнита ход, не выходит ли точка за пределы, не занята-ли уже точка
def isItTurn(fld, who, point):
	(x, y) = point;
	if (x<0 or y<0):
		return false;
	l = getLfield(fld);
	if ((x+1) > l or (y+1) > l):
		return false;
		
	if (fld[y*l + x] <> Fchar):
		return false;
	
	whoseTrn = whoseTurn(fld);
	if (whoseTrn <> who):
		return false;
	return true;

#ставит знак 'who' в точку (x, y), делать это надо только проверив возможность хода
def makeTurn(fld, who, point): 
	(x, y) = point;
	l = getLfield(fld);
	fld[y*l + x] = who;

#смотрит на поле и говорит кто выиграл (прочерк в случае, если еще никто), знак NBchar ('', ничья) если свободных клеток не осталось
#minChain - это сколько нужно поставить в ряд, чтобы выиграть, по-умолчанию - сторона поля
#делает это так: беруться все горизонтали, вертикали, диагонали и для каждой смотрят - нет ли достаточного кол-ва символов подряд
def whoseWin(fld, minChain=0):
	Fcount = sum([1 for x in fld if x <> Fchar]);
	if (Fcount >= len(fld)):
		return NBchar;
	
	if minChain == 0:
		minChain = getLfield(fld);
	chains = _getChains(fld, minChain);
	for chain in chains:
		res = _lambda_chain_find(chain, minChain);
		if (res <> Fchar):
			return res;
	return Fchar;

#для данной цепочки символов может ответить на вопрос - есть ли там достаточное для победы
#кол-во одинаковых символов подряд 
#todo: надо б переделать в lambda, или в FP-style выражение
def _lambda_chain_find(chain, minChain): 
	res = Fchar;
	count = 0;
	for c in chain:
		if (c == Fchar):
			res = Fchar;
			count = 0;
		if (c == res):
			count += 1;
			if (count >= minChain):
				return res;
		else:
			res = c;
			count = 1;
	return Fchar;

#для данного поля возвращает все вертикали, горизонтали, так вертикали, длтна которых не меньше, чем minChain
def _getChains(fld, minChain): 
	res = [];
	l = getLfield(fld);
	for i in range(l):
		res.append(_getChain(fld, 0, i, +1, 0));
		res.append(_getChain(fld, i, 0, 0, +1));
	for x in range(l-minChain+1):
		for y in range(l-minChain+1):
			if ((x==0)or(y==0)): 
				res.append(_getChain(fld, x, y, +1, +1));
			if ((x==0)or(y==0)): 
				res.append(_getChain(fld, l-x-1, y, -1, +1));
	return res;

#получает конкретную вертикаль или горизонталь и т.д.:
#для поля и данной точки спускается с указанными приращениями вниз, пока не дойдет до какой-нибудь из границ, все полученные точки возвращает
def _getChain(fld, x, y, dx, dy): 
	res = [];
	l = getLfield(fld);
		
	while x < l and y < l and x>=0 and y>=0:
		res.append(fld[y*l+x]);
		x += dx;
		y += dy;
	return res;
